package com.passion.exception;

import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.NotRoleException;
import cn.dev33.satoken.exception.SaTokenException;
import com.passion.common.base.ResultVO;
import com.passion.common.exception.MyException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

import java.util.List;

/**
 * 类描述：全局异常捕获处理器
 *
 * @author ZhuYin
 * @since 2024年12月25日
 */
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 处理@Valid 或 @Validated 参数校验异常
     * @param e 参数异常
     * @return 提示语
     */
    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler({MethodArgumentNotValidException.class, MissingServletRequestParameterException.class})
    public ResultVO<String> paramExceptionHandler(MethodArgumentNotValidException e) {
        BindingResult exceptions = e.getBindingResult();
        if (exceptions.hasErrors()) {
            List<ObjectError> errors = exceptions.getAllErrors();
            if (CollectionUtils.isNotEmpty(errors)) {
                FieldError fieldError = (FieldError) errors.get(0);
                log.error("参数校验异常=>字段:{},错误信息:{}", fieldError.getField(), fieldError.getDefaultMessage(), e);
                return ResultVO.getError(HttpStatus.BAD_REQUEST.value(), fieldError.getDefaultMessage());
            }
        }
        return ResultVO.getError(HttpStatus.BAD_REQUEST.value(), "请求参数错误");
    }

    /**
     * 处理自定义异常
     */
    @ExceptionHandler(value = MyException.class)
    @ResponseBody
    public ResultVO<?> baseExceptionHandler(MyException e) {
        log.error("自定义异常：code={}, message={} ", e.getCode() == null ? 500 : e.getCode(), e.getMessage(), e);
        return ResultVO.getError(e.getCode(), e.getMessage());
    }

    /**
     * 方法说明：处理sa-token相关的异常
     *
     * @param e 异常
     * @return 返回包装后的异常信息
     * @author ZhuYin
     * @since 2024年12月26日
     */
    @ExceptionHandler(value = SaTokenException.class)
    @ResponseBody
    public ResultVO<?> saTokenExceptionHandler(Exception e) {
        log.error("sa-token校验异常：{}", e.getMessage());
        String msg = e.getMessage();
        if (e instanceof NotLoginException) {
            if (e.getMessage().contains("：")) {
                msg = e.getMessage().substring(0, e.getMessage().indexOf("："));
            }
        } else if (e instanceof NotRoleException) {
            msg = "无此角色权限";
        } else if (e instanceof NotPermissionException) {
            msg = "无此权限";
        }
        return ResultVO.getError(401, msg);
    }

    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public ResultVO<?> exceptionHandler(Exception e) {
        String msg;
        if (e instanceof HttpMessageNotReadableException || e instanceof HttpMediaTypeNotSupportedException) {
            // 接口定义方式错误
            msg = "接口参数定义方式错误，请使用对象封装参数，然后传json格式参数调用";
            log.error("{}", msg, e);
        } else if (e instanceof HttpRequestMethodNotSupportedException) {
            msg = "请求方式错误";
            log.error("{}：{}", msg, e.getMessage());
        } else {
            msg = "系统繁忙，请稍候再试";
            log.error("其他异常：{}", e.getMessage(), e);
        }
        return ResultVO.getError(HttpStatus.INTERNAL_SERVER_ERROR.value(), msg);
    }

}
